看电影

Time Limit: 10 Sec Memory Limit: 259 MB

Description

到了难得的假期,小白班上组织大家去看电影。但由于假期里看电影的人太多,很难做到让全班看上同一场电影,最后大家在一个偏僻的小胡同里找到了一家电影院。但这家电影院分配座位的方式很特殊,具体方式如下: 1. 电影院的座位共有K个,并被标号为1…K,每个人买完票后会被随机指定一个座位,具体来说是从1…K中等可能的随机选取一个正整数,设其为L。 2. 如果编号L的座位是空位,则这个座位就分配给此人,否则将L加一,继续前面的步骤。 3. 如果在第二步中不存在编号L的座位,则该人只能站着看电影,即所谓的站票。小白班上共有N人(包括小白自己),作为数学爱好者,小白想知道全班都能够有座位的概率是多少。

Input

输入文件第一行有且只有一个正整数T,表示测试数据的组数。 第2~T+1行,每行两个正整数N,K,用单个空格隔开,其含义同题目描述。

Output

输出文件共包含T行。第i行应包含两个用空格隔开的整数A,B,表示输入文件中的第i组数据的答案为A/B。(注意,这里要求将答案化为既约分数)

Sample Input

​   3
​   1 1
​   2 1
​   2 2

Sample Output

1 1
  0 1
  3 4

HINT

对于100%的数据 T<=50,N,K<=200

Main idea

有n个人,k个位置,询问按照以下坐法使得所有人都有位置坐的概率是多少。(坐法:每个人随机一个位置,如果这个位置有人那一直就往后坐,如果后面都有人了则不可行)

Source

运用组合数学,首先我们知道n个人k个位置的总方案数是k^n,然后我们考虑一下怎么求出可行的方案,发现直接做的话无解与有解的两个情况不好考虑,怎么办呢?

我们发现可以考虑一下多加一个空位置使其构成一个环,那么这时候每个位置都必定是有解的方案数就是(k+1)^n。再考虑如何删掉重复的情况,由于我们加入了一个位置,那么除去经过这个位置的情况显然是方案数/(k+1),那么现在方案数就是(k+1)^n/(k+1),然后乘上有几个空位置即可。最后答案就是:( (k+1)^n/(k+1)*(k+1-n) ) / (k^n)

我们发现这个现在求出来的方案数比较大,但是又看见了n,k<=200,想到了数字n的质因数和n^k的质因数是一样的(所以这时候质因数肯定都是200以内的质数),所以我们乘的时候直接质因数分解,然后两个数字的质因数去重,最后用一个高精度乘起来输出即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include<bits/stdc++.h>
using namespace std;

const int ONE=201;

int T;
int n,k;
int prime[ONE],cnt;
int p[ONE][3];
int record,x;

struct power
{
int num[10001],len;
void print()
{
for(int i=len;i>=1;i--)
printf("%d",num[i]);
}

friend power operator *(power a,power b)
{
power c;
c.len=a.len+b.len;
for(int i=1;i<=c.len;i++) c.num[i]=0;

for(int i=1;i<=a.len;i++)
{
x=0;
for(int j=1;j<=b.len;j++)
{
c.num[i+j-1]=c.num[i+j-1] + x + a.num[i]*b.num[j];
x=c.num[i+j-1]/10;
c.num[i+j-1]%=10;
}
c.num[i+b.len]=x;
}

while(c.len>1 && !c.num[c.len]) c.len--;
return c;
}
}kd[3],pass;

void dealwith(int x)
{
for(int i=1;i<=pass.len;i++) pass.num[i]=0;
pass.len=0;
while(x)
{
pass.num[++pass.len]=x%10;
x/=10;
}
}

int get()
{
int res=1,Q=1;char c;
while( (c=getchar())<48 || c>57 )
if(c=='-')Q=-1;
res=c-48;
while( (c=getchar())>=48 && c<=57 )
res=res*10+c-48;
return res*Q;
}

int PD(int x)
{
for(int i=2;i<x;i++)
if(x%i==0) return 0;
return 1;
}

int Chai(int x,int PD)
{
if(x==1) return x;
for(int i=1;i<=cnt;i++)
{
if(!(x%prime[i]))
{
p[prime[i]][PD]++;
x=Chai(x/prime[i],PD);
break;
}
}
return x;
}

int Deal(int x,int m,int PD)
{
record=1;
for(int i=1;i<=m;i++)
{
record*=x;
record=Chai(record,PD);
}

if(PD==1)
{
record*=(x-m-1);
record=Chai(record,PD);
}

p[record][PD]++;
}

int main()
{
T=get();
for(int i=2;i<=200;i++)
if(PD(i)) prime[++cnt]=i;

while(T--)
{
n=get(); k=get();
if(n>k)
{
printf("0 1\n");
continue;
}

memset(p,0,sizeof(p));
Deal(k+1,n-1,1); Deal(k,n,2);
for(int i=1;i<=200;i++)
{
int x=min(p[i][1],p[i][2]);
p[i][1]-=x; p[i][2]-=x;
}

kd[1].len=kd[2].len=1;
kd[1].num[1]=kd[2].num[1]=1;
for(int i=1;i<=cnt;i++)
{
dealwith(prime[i]);
for(int t=1;t<=2;t++)
for(int j=1;j<=p[prime[i]][t];j++)
kd[t]=kd[t]*pass;
}

kd[1].print(); printf(" ");
kd[2].print(); printf("\n");

}
}